﻿using LD.Host;
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace LD
{
    public class TcpClientHelper : BaseCommunication, IDisposable
    {
        private TcpClient m_tcpClient;

        private NetworkStream m_stream;

        private Task m_receiveTask;

        private CancellationTokenSource m_receiveTaskCTS;

        private CancellationToken m_receiveTaskCT;

        private object m_eventLock = new object();

        public override event EventHandler<byte[]> OnMessageReceived;

        //public event ConnectionStatusDelegate OnConnectionStatus;

        private TcpIpParameModel m_tcpipParame;

        private ManualResetEvent m_waitReceiveData = new ManualResetEvent(false);

        public TcpClientHelper(TcpIpParameModel parame)
        {
            m_tcpipParame = parame;
        }
        public bool IsConnected
        {
            get
            {
                if (m_tcpClient == null)
                {
                    return false;
                }
                return m_tcpClient.Connected;
            }
        }
        protected void StartReceiveTask()
        {
            StopReceiveTask();
            m_receiveTaskCTS = new CancellationTokenSource();
            m_receiveTaskCT = m_receiveTaskCTS.Token;
            m_receiveTask = new Task(ReceiveTask, m_receiveTaskCT, TaskCreationOptions.LongRunning);
            m_receiveTask.Start();
        }
        protected void ReceiveTask()
        {
            try
            {
                byte[] array = new byte[102400];
                int num = 0;
                int result = 0;
                int num2 = 0;
                bool flag = false;
                int result2 = 0;
                int num3;
                int num4;
                int num5;

                num3 = 0;
                num4 = 0;
                num5 = 0;

                while (!m_receiveTaskCT.IsCancellationRequested)
                {
                    bool flag2 = false;
                    int count = ((num2 != 0) ? (num2 - num) : (num3 + 20 + 1 + num4 - num));
                    int num6;
                    try
                    {
                        num6 = Read(array, num, count);
                    }
                    catch
                    {
                        num6 = 0;
                    }
                    if (num6 == 0)
                    {
                        break;
                    }
                    num += num6;
                    if (result == 0 && num >= num3 + 20)
                    {
                        if (int.TryParse(Encoding.ASCII.GetString(array, num5, 4), out result) && int.TryParse(Encoding.ASCII.GetString(array, num5 + 4, 4), out result2))
                        {
                            if (result < 20 || result > 9999)
                            {
                                result = 0;
                                flag2 = true;
                            }
                            else if (result2 == 900)
                            {
                                flag = true;
                                num2 = num3 + result + num4;
                            }
                            else
                            {
                                result++;
                                flag = false;
                                num2 = num3 + result + num4;
                            }
                        }
                        else
                        {
                            flag2 = true;
                        }
                    }
                    if (!flag2 && num == num2)
                    {
                        if (flag || array[num2 - (num4 + 1)] == 0)
                        {
                            if (this.OnMessageReceived != null)
                            {
                                byte[] array2 = new byte[result];
                                Array.Copy(array, num3, array2, 0, result);
                                lock (m_eventLock)
                                {
                                    try
                                    {
                                        this.OnMessageReceived(this, array2);
                                        m_waitReceiveData.Set();
                                    }
                                    catch
                                    {
                                    }
                                }
                            }
                            flag2 = true;
                        }
                        else
                        {
                            flag2 = true;
                        }
                    }
                    if (flag2)
                    {
                        result = 0;
                        num2 = 0;
                        num = 0;
                    }
                }
            }
            catch (Exception)
            {
                Console.WriteLine("");
            }
            finally
            {
                if (!m_tcpClient.Connected)
                {
                    ReConnect();
                }
            }
        }
        protected void StopReceiveTask()
        {
            Task task = Interlocked.Exchange(ref m_receiveTask, null);
            if (task != null)
            {
                m_receiveTaskCTS.Cancel();
                task.Wait();
                task.Dispose();
                m_receiveTaskCTS.Dispose();
            }
        }
        public override void Connect()
        {
            Open(m_tcpipParame.TimeOut);
        }
        public override void Closed()
        {
            Dispose();
        }
        public override object Send(string content)
        {
            m_waitReceiveData.Reset();

            byte[] buff = Encoding.ASCII.GetBytes(content);
            Array.Resize(ref buff, buff.Length + 1);
            Write(buff, 0, buff.Length);

            var waitResult = m_waitReceiveData.WaitOne(2000);
            if (!waitResult)
                Log4.Warr($"【{m_tcpipParame.Name}】等5秒后未收到信息");

            return buff;
        }
        private void Open(int timeoutMs)
        {
            if (m_tcpClient != null && m_tcpClient.Connected)
            {
                m_tcpClient.Close();
            }
            m_tcpClient = new TcpClient();
            m_tcpClient.ReceiveTimeout = 0;
            m_tcpClient.SendTimeout = 2000;
            m_tcpClient.NoDelay = true;
            m_tcpClient.BeginConnect(m_tcpipParame.Ip, m_tcpipParame.Port, new AsyncCallback(ConnectAsync), m_tcpClient);
        }
        private void ConnectAsync(IAsyncResult ar)
        {
            var tcpClient = ar.AsyncState as TcpClient;

            try
            {
                if (tcpClient != null && tcpClient.Connected)
                {
                    tcpClient.EndConnect(ar);
                    m_stream = m_tcpClient.GetStream();
                    StartReceiveTask();
                    ConnectionStateEvent?.Invoke(m_tcpipParame.Name, Status.成功);
                }
            }
            catch (Exception)
            {
                ConnectionStateEvent?.Invoke(m_tcpipParame.Name, Status.失败);
            }
            finally
            {
                if (!tcpClient.Connected)
                    ReConnect();
            }
        }
        private void ReConnect()
        {
            Console.WriteLine($"【{this.m_tcpipParame.Name}】重连中。。。");
            try
            {
                ConnectionStateEvent?.Invoke(m_tcpipParame.Name, Status.失败);
                Connect();
            }
            catch (Exception)
            {
            }
        }
        public void Dispose()
        {
            m_stream?.Dispose();
            m_tcpClient?.Close();
        }
        public void Write(byte[] buffer, int offset, int count)
        {
            if (m_stream != null)
            {
                m_stream.Write(buffer, offset, count);
                m_stream.Flush();
            }
        }
        public int Read(byte[] buffer, int offset, int count)
        {
            if (m_stream != null && m_stream.CanRead)
            {
                return m_stream.Read(buffer, offset, count);
            }
            return 0;
        }
    }
}
